﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Runtime.Serialization;
using System.Windows.Automation;
using System.Globalization;

namespace PowerPatcher
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private PlugInHost plugHost;
        private System.Threading.Thread LoadThread;
        private IntPtr handle;
        
        private bool _patchState = false;
        public bool PatchState
        {
            get { return _patchState; }
            set
            {
                if (value == _patchState)
                    return;
                _patchState = value;
                Dispatcher.Invoke(new Action(() => { setEnabledRecursive(this, !value); }), null);
            }
        }

        private void setEnabledRecursive(object obj, bool Enabled)
        {
            if (obj is FrameworkElement)
            {
                FrameworkElement fe = (FrameworkElement)obj;
                if (fe.GetType() == typeof(Button))
                {
                    Button b = (Button)fe;
                    if ((string)b.Tag != "NoDisable")
                    {
                        b.IsEnabled = Enabled;
                    }
                }
                System.Collections.IEnumerable children = LogicalTreeHelper.GetChildren(fe);
                foreach (object child in children)
                    setEnabledRecursive(child, Enabled);
            }
        }

        public MainWindow()
        {
            Properties.Settings.Default.Reload();
            System.Threading.Thread.CurrentThread.CurrentUICulture = Properties.Settings.Default.Culture;
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            PatchState = true;
            handle = new System.Windows.Interop.WindowInteropHelper(Application.Current.MainWindow).Handle;
            PatcherVersLabel.Content = PowerPatcher.Msgs.LauncherVersion + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
            Helper.WriteLogLine("Preforming startup tasks. Main window loaded, handle " + handle.ToString() + ". Application version: " + (string)PatcherVersLabel.Content);
            Properties.Settings.Default.Reload();
            UpdateLvlCombo.SelectedValue = Properties.Settings.Default.UpdateLevel; //Accomodates a bug in the framework
            //switch (Properties.Settings.Default.Website)
            //{
            //    case 0:
            //        LoadWebPage(lFrame, "http://rh.scottketelaar.com/MabiServerStat/index.php");
            //        LoadWebPage(rFrame, "http://wiki.mabinogiworld.com/index.php?title=Wiki_Home/WikiUpdates&action=render");
            //        break;
            //    case 1:
            //        Grid.SetColumnSpan(lFrame, 2);
            //        LoadWebPage(lFrame, "http://nxcache.nexon.net/mabinogi/launcher/notice.html");
            //        break;
            //    default:
            //        lFrame.Visibility = System.Windows.Visibility.Hidden;
            //        rFrame.Visibility = System.Windows.Visibility.Hidden;
            //        break;
            //}

            if (string.IsNullOrWhiteSpace(PowerPatcher.Properties.Settings.Default.RightURL))
            {
                rFrame.Visibility = System.Windows.Visibility.Hidden;
                Grid.SetColumnSpan(lFrame, 2);
            }
            else
                LoadWebPage(rFrame, PowerPatcher.Properties.Settings.Default.RightURL);
            if (string.IsNullOrWhiteSpace(PowerPatcher.Properties.Settings.Default.LeftURL))
                lFrame.Visibility = System.Windows.Visibility.Hidden;
            else
                LoadWebPage(lFrame, PowerPatcher.Properties.Settings.Default.LeftURL);

            System.IO.Directory.CreateDirectory("Localization\\Langs\\enUS");
            string[] Langfiles = System.IO.Directory.GetDirectories("Localization\\Langs");
            foreach (string f in Langfiles)
            {
                try
                {
                    CultureInfo c = CultureInfo.CreateSpecificCulture(System.IO.Path.GetFileName(f));
                    ComboBoxItem cbi = new ComboBoxItem();
                    cbi.Content = c.NativeName + "\t" + c.EnglishName;
                    cbi.Tag = c;
                    langs.Items.Add(cbi);
                    if (c.ThreeLetterISOLanguageName == Properties.Settings.Default.Culture.ThreeLetterISOLanguageName)
                        langs.SelectedItem = cbi;
                }
                catch (Exception ex) { Helper.WriteLogLine(ex.Message); }
            }

            LoadThread = new System.Threading.Thread(IntensiveLoad);
            LoadThread.CurrentUICulture = Properties.Settings.Default.Culture;
            Helper.WriteLogLine("Window setup complete. Executing intensive loading thread.");
            LoadThread.Start();
        }

        private UpdateChecker.UpdateFilter GetUpdateLvl()
        {
        	return (UpdateChecker.UpdateFilter)Enum.Parse(typeof(UpdateChecker.UpdateFilter), Properties.Settings.Default.UpdateLevel);
        }
        
        int Done = 0;
        private void IntensiveLoad()
        {
            
        	UpdateChecker.CheckForUpdates("https://hg01.codeplex.com/powerpatcher/raw-file/tip/releases.xml", GetUpdateLvl(),
                new Action<double,string>(UpdateMainProg), new Action<double,string>(UpdateSubProg1));

            plugHost = new PlugInHost(this);
            Helper.WriteLogLine("===Initializing Plugins");
            UpdateMainProg(0, String.Format(PowerPatcher.Msgs.InitializingPluginsX0X1, Done, plugHost.PlugIns.Count));
            System.Collections.ObjectModel.Collection<PowerPatcherInterop.iPlugIn>[] parts = Helper.Partition<PowerPatcherInterop.iPlugIn>(plugHost.PlugIns, 2);
            Action<System.Collections.ObjectModel.Collection<PowerPatcherInterop.iPlugIn>, Action<double, string>> t1 = 
                new Action<System.Collections.ObjectModel.Collection<PowerPatcherInterop.iPlugIn>, Action<double, string>>(LoadList);
            Action<System.Collections.ObjectModel.Collection<PowerPatcherInterop.iPlugIn>, Action<double, string>> t2 = 
                new Action<System.Collections.ObjectModel.Collection<PowerPatcherInterop.iPlugIn>, Action<double, string>>(LoadList);
            t1.BeginInvoke(parts[0], new Action<double, string>(UpdateSubProg1), null, null);
            t2.BeginInvoke(parts[1], new Action<double, string>(UpdateSubProg2), null, null);
            while (Done != plugHost.PlugIns.Count)
                DoEvents();
            UpdateMainProg(1, PowerPatcher.Msgs.LoadingComplete);
            Helper.WriteLogLine("Plugin load Complete");
            PatchState = false;
            RunAllPlugins();
        }

        private void LoadList(System.Collections.ObjectModel.Collection<PowerPatcherInterop.iPlugIn> Plugs, Action<double, string> Prog)
        {
            System.Threading.Thread.CurrentThread.CurrentUICulture = PowerPatcher.Properties.Settings.Default.Culture;
            foreach (PowerPatcherInterop.iPlugIn plug in Plugs)
            {
            	string plugPath = plugHost.GetPlugPath(plug);
                Helper.WriteLogLine("Creating UI for plugin: " + plug.Name);
                try
                {
                	Prog.Invoke(0,PowerPatcher.Msgs.Initializing + plug.Name);
                	PowerPatcherInterop.InitInfo info = new PowerPatcherInterop.InitInfo();
                	info.AddInLocation = plugPath;
                	info.MainUpdater = new Action<double, string>(UpdateMainProg);
                	info.SupUpdaters = new Action<double, string>[]
                    {
                		new Action<double, string>(UpdateSubProg1),
                		new Action<double, string>(UpdateSubProg2)
                    };
                	info.LogString = new Action<string, bool>(LogString);
                    info.SetPatchState = new Action<bool>((b) => { PatchState = b; });
                    info.GetPatchState = new Func<bool>(() => { return this.PatchState; });
                    info.GetShowDetailedError = new Func<bool>(() => { return PowerPatcher.Properties.Settings.Default.ShowDetailedErrors; });
                    info.LogException = new Action<Exception, bool>((e, b) =>
                    {
                        LogString(Helper.ResolveExcpt(e), b);
                    });

                	plug.Initialize(info);
                	
                    Prog.Invoke(.5, PowerPatcher.Msgs.CreatingUIForPlugin + plug.Name);
                    Dispatcher.Invoke(new Action(() =>
                    {
                    TabItem tab = new TabItem();
                    tab.Name = "p" + plug.GetGUID().ToString().Replace("-", "_");
                    tab.Header = plug.Name;
                    tab.Content = plug.GetAddInUI();
                        int pos = 0;
                        for (int c = TabHost.Items.Count; pos < c; pos++)
                        {
                            if (c != TabHost.Items.Count)
                            {
                                pos = -1;
                                c = TabHost.Items.Count; //Something else was added
                                continue;
                            }
                            string plugHeader = (string)((TabItem)TabHost.Items[pos]).Header;
                            string plugName = ((TabItem)TabHost.Items[pos]).Name;
                            if (plugName[0] == 'p')
                            {
                                if (plug.Name.CompareTo(plugHeader) < 0)
                                {
                                    break;
                                }
                            }
                        }
                        TabHost.Items.Insert(pos, tab);
                    }));
                }
                catch (Exception ex)
                {
                    Helper.WriteLogLine("ERROR: Exception during initialization of the \"" + plug.Name + "\" plugin: " + ex.Message);
                }
                Prog.Invoke(1, PowerPatcher.Msgs.Done);
                System.Threading.Interlocked.Increment(ref Done);
                UpdateMainProg(Done / (double)plugHost.PlugIns.Count, String.Format(PowerPatcher.Msgs.InitializingPluginsX0X1, Done, plugHost.PlugIns.Count));
            }
        }

        private void LoadWebPage(System.Windows.Forms.Integration.WindowsFormsHost f, string URL)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                ((System.Windows.Forms.WebBrowser)f.Child).Navigate(new Uri(URL));
            }), System.Windows.Threading.DispatcherPriority.Background);
        }
        
        private void OnExit()
        {
        	Properties.Settings.Default.Save();
        }

        private void TabHost_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            this.InvalidateVisual();
        }

        public void UpdateMainProg(double Value, string Text)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                if (Value >= 0)
                    MainProgBar.Value = Value;
                if (Text != null)
                    MainProgLab.Content = Text;
                //this.InvalidateVisual();
            }), System.Windows.Threading.DispatcherPriority.Render, null);
        }
        public void UpdateSubProg1(double Value, string Text)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                if (Value >= 0)
                    SubProg1Bar.Value = Value;
                if (Text != null)
                    SubProg1Lab.Content = Text;
                //this.InvalidateVisual();
            }), System.Windows.Threading.DispatcherPriority.Render, null);
        }
        public void UpdateSubProg2(double Value, string Text)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                if (Value >= 0)
                    SubProg2Bar.Value = Value;
                if (Text != null)
                    SubProg2Lab.Content = Text;
                //this.InvalidateVisual();
            }), System.Windows.Threading.DispatcherPriority.Render, null);
        }
        public void LogString(string text, bool ShowMsgBox)
        {
            Helper.WriteLogLine(text);
            if (ShowMsgBox)
            	MessageBox.Show(text, PowerPatcher.Msgs.Error, MessageBoxButton.OK, MessageBoxImage.Error);
        }

        static void DoEvents()
        {
            DispatcherFrame frame = new DispatcherFrame(true);
            Dispatcher.CurrentDispatcher.BeginInvoke
            (
            DispatcherPriority.Background,
            (System.Threading.SendOrPostCallback)delegate(object arg)
            {
                var f = arg as DispatcherFrame;
                f.Continue = false;
            },
            frame
            );
            Dispatcher.PushFrame(frame);
        }

        private void RunAllPlugins()
        {
            Helper.WriteLogLine("Run all plugins started.");
            foreach (PowerPatcherInterop.iPlugIn plug in plugHost.PlugIns)
            {
                Helper.WriteLogLine("Running " + plug.Name);
                plug.RunPlugin();
            }
            Helper.WriteLogLine("Run all plugins finished.");
            UpdateMainProg(1, PowerPatcher.Msgs.RunAllPluginsComplete);
            UpdateSubProg1(1, "");
            UpdateSubProg2(2, "");
            if (Properties.Settings.Default.StartAfterPatch)
                StartGame(null, null);
        }

        private void StartGame(object sender, RoutedEventArgs e)
        {
            try
            {
                string args = Properties.Settings.Default.ClientStartArgs;
                string[] sArgs = (string[])Application.Current.Properties["StartArgs"];
                foreach (string s in sArgs)
                    if (s.StartsWith("/P:") || s.StartsWith("/p:"))
                        args += " " + s;
                Helper.WriteLogLine("Starting Mabinogi. Args: " + args);
                System.Diagnostics.Process.Start("client.exe", args);
                Environment.Exit(0);
            }
            catch (Exception ex)
            {
                LogString(string.Format(PowerPatcher.Msgs.X0OccuredWhileAttempingToLaunchMabinogiX1, ex.GetType().ToString(), Helper.ResolveExcpt(ex)), true);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RunAllPlugins();
        }

        private void PPWindow_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown(0);
            Environment.Exit(0);
        }

        private void langs_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            PowerPatcher.Properties.Settings.Default.Culture = (CultureInfo)((ComboBoxItem)langs.SelectedItem).Tag;
            PowerPatcher.Properties.Settings.Default.Save();
        }
    }
        #region CheckForUpdates
        class UpdateChecker
        {
            public enum ReleaseState
            {
                //Make critical 0?
                Critical = 1,
                Stable = 2,
                Beta = 4,
                Alpha = 8,
            }
            public enum UpdateFilter
            {
                Critical = ReleaseState.Critical,
                Stable = Critical | ReleaseState.Stable,
                Beta = Stable | ReleaseState.Beta,
                Alpha = Beta | ReleaseState.Alpha
            }
            public static void CheckForUpdates(string feedUrl, UpdateFilter filter, Action<double, string>MainUpdater, Action<double, string>SubUpdater)
            {
                Helper.WriteLogLine("Checking for updates. Initializing feed parser.");
                MainUpdater.Invoke(0, PowerPatcher.Msgs.CheckingForUpdatesToPowerPatcher);
                SubUpdater.Invoke(0, PowerPatcher.Msgs.InitializingFeedParser);
                    // NOTE: Requires a reference to System.ServiceModel.dll
                System.ServiceModel.Syndication.Atom10FeedFormatter formatter = new System.ServiceModel.Syndication.Atom10FeedFormatter();

                Helper.WriteLogLine("Retriving feed from: " + feedUrl);
                SubUpdater.Invoke(.33, PowerPatcher.Msgs.RetreivingReleaseFeed); 
                // Read the feed
                System.Xml.XmlReader reader;
                try
                {
                    reader = System.Xml.XmlReader.Create(feedUrl);
                }
                catch (Exception ex)
                {
                    Helper.WriteLogLine("Error retriving feed: " + ex.ToString());
                    throw;
                }
                formatter.ReadFrom(reader);

                SubUpdater.Invoke(.66, PowerPatcher.Msgs.SearchingForUpdates);
                // Get the version specified in AssemblyInfo.cs
                Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
 
                // Linq magic
                System.ServiceModel.Syndication.SyndicationItem update = (from i in formatter.Feed.Items
                              where new Version(i.Title.Text) > currentVersion &&
                                  i.Categories.Any(c => CanUpgradeTo(c.Name, filter))
                              orderby i.LastUpdatedTime descending
                              select i).FirstOrDefault();

                SubUpdater.Invoke(1, PowerPatcher.Msgs.Complete);
                if (update != null)
                {
                    Helper.WriteLogLine("Update available (" + update.Title.Text + "). Marked as " + update.Categories[0].Name);
                    if (((UpdateFilter)Enum.Parse(typeof(UpdateFilter), update.Categories[0].Name) == UpdateFilter.Critical) ||
                        (MessageBox.Show(string.Format(PowerPatcher.Msgs.AnUpdateToPowerPatcherIsAvailableTheUpdate,
                        update.Categories[0].Name, update.Title.Text, update.LastUpdatedTime.Date.ToShortDateString(), currentVersion), PowerPatcher.Msgs.Update, MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes))
                    {
                        Helper.WriteLogLine("Downloading update");
                        MainUpdater.Invoke(.5, PowerPatcher.Msgs.DownloadingPowerPatcherVersion + update.Title.Text);
                        Uri downloadUrl = update.Links.Single().Uri;
                        DownloadAndRunUpdate(downloadUrl, SubUpdater);
                    }
                    else
                        Helper.WriteLogLine("Update canceled");
                }
                else
                    Helper.WriteLogLine("Most up-to-date version");
            }

            static bool CanUpgradeTo(string status, UpdateFilter filter)
            {
                // Bitwise magic
                return ((((int)filter) & ((int)Enum.Parse(typeof(UpdateFilter), status, true))) != 0);
            }

            //static Action<double, string> Sub;
            //static volatile bool Complete;
            private static void DownloadAndRunUpdate(Uri url, Action<double, string> sP)
            {
            //    Sub = sP;
            //    string exepath = System.IO.Path.GetTempFileName();
            //    System.IO.File.Move(exepath, System.IO.Path.Combine(System.IO.Path.GetDirectoryName(exepath), System.IO.Path.GetFileNameWithoutExtension(exepath) + ".exe"));
            //    exepath = System.IO.Path.GetFileNameWithoutExtension(exepath) + ".exe";
            //    using (System.Net.WebClient wc = new System.Net.WebClient())
            //    {
            //        wc.DownloadProgressChanged += new System.Net.DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            //        wc.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wc_DownloadFileCompleted);
            //        Complete = false;
            //        wc.DownloadFileAsync(url, exepath);
            //        while (!Complete)
            //            DoEvents();
            //    }
            //    System.Diagnostics.Process.Start(exepath, System.Diagnostics.Process.GetCurrentProcess().Id.ToString());
                System.Diagnostics.Process.Start(url.ToString());
                Environment.Exit(2);
            }

            //static void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
            //{
            //    Complete = true;
            //}

            //static void wc_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
            //{
            //    Sub.Invoke(e.ProgressPercentage / 100.0, "(" + e.BytesReceived + "/" + e.TotalBytesToReceive + ")"); 
            //}
        }

        #endregion

    public class SortViaHeaderName : IComparer<TabItem>
    {
        public int Compare(TabItem x, TabItem y)
        {
            string xh, yh;
            xh = (string)x.Header;
            yh = (string)y.Header;
            if (xh[0] == 'p')
            {
                if (yh[0] == 'p')
                    return xh.CompareTo(yh);
                else
                    return -1;
            }
            else if (yh[0] == 'p')
                return 1;
            else
                return 0;
        }
    }
}
